#ifndef _REP_HPP_
#define _REP_HPP_
#include "gc.h"
#include "gc_cpp.h"
#include "gc_allocator.h"
#include <sys/types.h>
#include <utility>
#include <list>
#include <vector>
#include <string>
#include <ext/hash_map>
 
#define DEFAULT_MAX_HEAPSIZE (0x01<<16)
#define DEFAULT_START_HEAPSIZE (0x01<<8)

using namespace std; 
using namespace __gnu_cxx;


namespace rep{

  //boxed things
  class Slot;
  class Type;
  class FrameType;
  class UnboundFrameType;
  class Lambda;
  class Block;
  class Continuation;
  class Environment;
  class Cons;

  //wrappers for boxed things.
  class TypeHelper;
  class FrameHelper;
  class StringHelper;
  class BlockHelper;
  class ContinuationHelper;
  class EnvironmentHelper;

  //wrappers for unboxed, immutable things
  class SymbolHelper;
  class IntegerHelper;

  /**
   * A type of function for lookups-- the first arg is the source, the
   * second arg is the symbol to lookup, the third arg is the
   * destination.
   */
  typedef void (*LookupFun)(Slot*, Slot*, Slot*);


  /**
   * A type of function for getting the size of things
   */
  typedef size_t (*SizeFun)(Slot*);


  /**
   * Handles things that objects do, based on the slot pointers to
   * them
   */

  struct  ObjectHandler{
  public:
    LookupFun lookup;
    SizeFun size;
  };

  /**
   * This class wraps an object, and provides access to some basic
   * methods.
   */

  class TypeHelper{
  public:
    static size_t memSize(Slot* in);
    static Type* type(Slot* in);
    static string* toString(Slot* in);
    static int hashCode(Slot* in);
    static Slot* rawLexicalScope(Slot* in);
    static void assign(Slot* to, Type* rvalue);
  };

  class FrameHelper{
  public:
    static size_t memSize(Slot* in);
    static Type* type(Slot* in);
    static string* toString(Slot* in);
    static int hashCode(Slot* in);
    static void assign(Slot* to, Slot* rvalue);

    static Slot* alloc(FrameType *typ);    
    static int size(Slot* in);
    /**
     * Looks up the slot at offst in this frame
     */
    static Slot* immDirectLookup(Slot* in, int offst);
    static Slot* directLookup(Slot* in, int offst);
    static Slot* immParent(Slot* in);
    static Slot* parent(Slot* in);
    /**
     * Looks up the slot generations up (this frame is 0) at the given
     * offset from that frame.
     */
    static Slot* immAncestralLookup(Slot* in, int generations, int offst);
    static Slot* ancestralLookup(Slot* in, int generations, int offst);

    static Continuation* immException(Slot* in);
    static Continuation* exception(Slot* in);


    /**
     * Ways of looking up the n different standard continuations from a lambda.
     */
    static Continuation* ret(Slot* in);
    static Continuation* except(Slot* in);
    static Continuation* yield(Slot* in);
    static Continuation* ifdone(Slot* in);
    static Continuation* br(Slot* in);
    static Continuation* cont(Slot* in);
  };

  class IntegerHelper{
  public:
    static size_t memSize(Slot* in);
    static Type* type(Slot* in);
    static string* toString(Slot* in);
    static int hashCode(Slot* in);
    static void assign(Slot* to, int rvalue);
  };


  class ConsHelper{
  public:
    static size_t memSize(Slot* in);
    static Type* type(Slot* in);
    static string* toString(Slot* in);
    static int hashCode(Slot* in);
    static void assign(Slot* to, Cons* rvalue);
  };

  
  /**
   * An object or frame is just an array of slots.  The tag of a slot
   * determines what type of data it has; after the tag is the data
   * element, which can directly store a 32-bit value, or a pointer to
   * another object.
   *
   * The first slot in an object/frame is used for bookeeping.  When
   * the object is in a freelist, the "obj" field of the first slot is
   * used as a "next" pointer.  
   */

  class Slot{
  public:

    enum SlotTag{
      VOID,
      INT,
      UINT,
      FRAME,
      TYPE,
      CONS,
      CONTINUE,
      STRING,
      LAMBDA,
      BOOLEAN
    };

    /*
     * Standard places for continuations--all except return are optional:
     */

    enum ContinuationPlacement{
      RETURN = 1,
      EXCEPT = 2,
      YIELD = 3,
      IFDONE = 4,
      BREAK = 5,
      CONT = 6
    };

    enum ContinuationFlags{
      RETURN_FLAG = 0x01,
      EXCEPT_FLAG = 0x02,
      YIELD_FLAG = 0x04,
      IFDONE_FLAG = 0x08,
      BREAK_FLAG = 0x10,
      CONT_FLAG = 0x20,
    };

    union{
      SlotTag tag; // this is used as a type tag
      int32_t sz;
    };
    union {
      void* pointer;      
      int32_t i;
      uint32_t u;
      Slot* obj;
      Type* type;
      FrameType* ftype;
      Cons* cell;
      Continuation* cont;
      Lambda* proc;
      string* str;
      bool b;
    };
  };

  class Cons{
  public:
    Slot car;
    Slot cdr;
    Cons* pop(Slot* fill);
    Cons* push(const Slot& in);
  };


  class SymbolHelper{
  public:
    static string* getName(Slot* in);
    static size_t memSize(Slot* in);
    static Type* type(Slot* in);
    static string* toString(Slot* in);
    static int hashCode(Slot* in);
    static void assign(Slot* to, int rvalue);
  private:
    static hash_map<uint32_t, string> find_string;
    static hash_map<string, uint32_t> find_int;
  };


  class Type{
  public:
    Type(){}
    Type(Type* par){parent = par;}
    Type* parent; 
  };

  class UnboundFrameType : public Type{
  public:
    UnboundFrameType(){}
    UnboundFrameType(int sz, Type* par) :
      Type(par),
      numSlots(sz),
      bindings(NULL){}
    UnboundFrameType(int sz, Type* par, int* bind) :
      Type(par),
      numSlots(sz),
      bindings(bind){}
    int numSlots;
    int * bindings; //the nth argument is bound to the bindings[n[]th
                    //slot
    FrameType* bind(Slot* env);
  };

  class FrameType : public Type{
  public:
    FrameType(){}
    FrameType(Slot* lex, int sz, Type* par, int* bind, unsigned conts = 
              Slot::RETURN_FLAG | Slot::EXCEPT_FLAG | Slot::YIELD_FLAG): 
      Type(par),
      bindings(bind),
      lexicalEnvironment (lex),
      numSlots(sz),
      requiredContinuations(conts)
      {
    }
    int* bindings;
    Slot* lexicalEnvironment;
    int numSlots;
    unsigned requiredContinuations; //flags to describe which
                                    //continuations are required.
    bool requiresReturn();
    bool requiresExcept();
    bool requiresYield();
    bool requiresIfDone();
    bool requiresBreak();
    bool requiresCont();
    Cons* packArgs(Cons* stack, Slot* frame, int howmany);
  };



};
#endif //_REP_HPP_
